using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.DirectX;
using Microsoft.DirectX.Direct3D;

namespace Verlet
{
    class Link : Constraint
    {
        public Link(Node n1, Node n2, float length, float radius)
        {
            node1 = n1;
            node2 = n2;
            this.radius = radius;
            this.length = length;
            this.lengthsq = length * length;
        }
        public float length, radius, lengthsq;
        public Node node1, node2;

        public override void Constrain()
        {
            Vector3 delta = node1.Position - node2.Position;
            delta *= lengthsq / (delta.LengthSq() + lengthsq) - 0.5f;
            node1.Position += delta;
            node2.Position -= delta;
        }

        public static bool renderLines = false;
        static CustomVertex.PositionColored[] lineBuffer = new CustomVertex.PositionColored[2];

        public static void DrawLine(Device d, Vector3 from, Vector3 to, ColorValue color) {
            d.Transform.World = Matrix.Identity;
                d.VertexFormat = CustomVertex.PositionColored.Format;
                lineBuffer[0].Position = from;
                lineBuffer[0].Color = color.ToArgb();
                lineBuffer[1].Position = to;
                lineBuffer[1].Color = color.ToArgb();
                d.DrawUserPrimitives(PrimitiveType.LineList, 1, lineBuffer);
        }

        //
        // Render link as a cylinder (or line if renderLines is true)
        //
        public void Draw(Device d)
        {
            if (renderLines)
            {
                lineBuffer[0].Position = node1.Position;
                lineBuffer[0].Color = -1;
                lineBuffer[1].Position = node2.Position;
                lineBuffer[1].Color = -1;
                d.DrawUserPrimitives(PrimitiveType.LineList, 1, lineBuffer);
            }
            else
            {
                Vector3 offset = node2.Position - node1.Position;
                Vector3 b1 = Vector3.Cross(offset, new Vector3(0.0f, 1.0f, 0.0f));
                if (b1.LengthSq() < 0.001)
                    b1 = Vector3.Cross(offset, new Vector3(1, 0, 0));
                b1.Normalize();
                //b1 *= 0.2f;
                Vector3 b2 = Vector3.Cross(offset, b1);
                b2.Normalize();
                //b2 *= 0.2f;
                //b1 *= radius;
                //b2 *= radius;
                Matrix transform = Matrix.Translation(node1.Position + offset * 0.5f);
                transform.M11 = b1.X;
                transform.M12 = b1.Y;
                transform.M13 = b1.Z;
                transform.M21 = b2.X;
                transform.M22 = b2.Y;
                transform.M23 = b2.Z;
                transform.M31 = offset.X;
                transform.M32 = offset.Y;
                transform.M33 = offset.Z;
                d.Transform.World = transform;
                Material m = new Material();
                m.DiffuseColor = new ColorValue(255, 0, 0);
                //m.AmbientColor = m.DiffuseColor;
                d.Material = m;
                cylinder.DrawSubset(0);
            }
        }

        static Device lastDevice = null;
        static Mesh cylinder;
        public static void PrepareDevice(Device d)
        {
            if (d == lastDevice)
                return;
            cylinder = Mesh.Cylinder(d, 0.1f, 0.1f, 1.0f, 10, 1);
            //cylinder = Mesh.Sphere(d, 0.5f, 5, 5);
        }
    }

    class DistanceLowerBound : Link
    {
        public DistanceLowerBound(Node n1, Node n2, float length, float radius)
            : base(n1, n2, length, radius)
        { }

        public override void Constrain()
        {
            Vector3 delta = node1.Position - node2.Position;
            float ls = delta.LengthSq();
            if (ls < lengthsq)
            {
                delta *= lengthsq / (ls + lengthsq) - 0.5f;
                node1.Position += delta;
                node2.Position -= delta;
            }
        }
    }

    class LinkWithMidpoint : Link
    {
        public Node midpoint;
        public LinkWithMidpoint(Node n1, Node midpoint, Node n2, float length, float radius)
            : base(n1, n2, length, radius)
        {
            this.midpoint = midpoint;
        }

        public override void Constrain()
        {
            Vector3 realMid = (node2.Position + node1.Position)*0.5f;
            Vector3 offset = realMid - midpoint.Position;
            Vector3 qoff = 0.25f * offset;
            midpoint.Position += 0.5f * offset;
            node1.Position -= qoff;
            node2.Position -= qoff;

            base.Constrain();
        }
    }
}
